using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Xml;

namespace Stratman.DesktopProcess.DatabaseSynchronizer
{
    /// <summary>
    /// Main program class for the DatabaseSynchronizer; loads up the necessary project class and
    /// calls Execute() to perform the synchronization.
    /// </summary>
    public class Program
    {
        /// <summary>
        /// Usage string that displays the various command line parameters and their options.
        /// </summary>
        protected const string usageString = "\nUsage: DatabaseSynchronizer.exe /project:projectFile [/environment:environment]\n   [/consoleOutputLevel:Verbose|Information|Warning|Error|Critical]\n   [/log:logFile] [/logFormat:Text|XML]\n   [/logOutputLevel:Verbose|Information|Warning|Error|Critical]\n   [/ignoreManualScriptErrors] [/noChanges] [/scc:SVN]";

        /// <summary>
        /// Entry point for the DatabaseSynchronizer.
        /// </summary>
        /// <param name="args">
        /// Arguments for this program that were passed in on the command line.
        /// </param>
        public static void Main(string[] args)
        {
            string projectFilePath = "";
            SourceLevels consoleOutputLevel = SourceLevels.Verbose;
            SourceLevels logOutputLevel = SourceLevels.Verbose;
            StreamWriter logWriter = null;
            TraceListener logTraceListener = null;
            string logFormat = "xml";

            // Instantiate the variables from the command line arguments
            foreach (string argument in args)
            {
                if (argument.ToLower().StartsWith("/project:"))
                {
                    projectFilePath = argument.Substring(9);
                    projectFilePath = projectFilePath.Replace("\"", "");
                }

                else if (argument.ToLower().StartsWith("/log:"))
                    logWriter = new StreamWriter(new FileStream(argument.Substring(5).Replace("\"", ""), FileMode.Create));

                else if (argument.ToLower().StartsWith("/logformat:"))
                {
                    if (argument.ToLower().Substring(11) == "text" || argument.ToLower().Substring(11) == "xml")
                        logFormat = argument.ToLower().Substring(11);

                    else
                    {
                        Console.WriteLine(usageString);
                        return;
                    }
                }

                else if (argument.ToLower().StartsWith("/consoleoutputlevel:") || argument.ToLower().StartsWith("/logoutputlevel:"))
                {
                    if (!Enum.IsDefined(typeof(SourceLevels), argument.Substring(argument.IndexOf(':') + 1)))
                    {
                        Console.WriteLine(usageString);
                        return;
                    }

                    if (argument.ToLower().StartsWith("/consoleoutputlevel:"))
                        consoleOutputLevel = (SourceLevels)Enum.Parse(typeof(SourceLevels), argument.Substring(argument.IndexOf(':') + 1));

                    else
                        logOutputLevel = (SourceLevels)Enum.Parse(typeof(SourceLevels), argument.Substring(argument.IndexOf(':') + 1));
                }
            }

            // Create the TraceListener object responsible for writing entries to the log file, if
            // necessary
            if (logWriter != null)
            {
                if (logFormat == "text")
                    logTraceListener = new BasicTextWriterTraceListener(logWriter.BaseStream);

                // If this is an XML log file, write the opening tag for the root element
                else
                {
                    logWriter.Write("<E2ETraceEvents>");
                    logWriter.Flush();

                    logTraceListener = new XmlWriterTraceListener(logWriter.BaseStream);
                }
            }

            // Make sure that the necessary command line arguments were provided
            if (projectFilePath == "")
            {
                Console.WriteLine(usageString);
                return;
            }

            FileInfo projectFile = new FileInfo(projectFilePath);

            // Set the current directory to that of the project file so that the file may contain
            // relative paths
            if (projectFile.Exists)
                Directory.SetCurrentDirectory(projectFile.DirectoryName);

            SynchronizerProject project = null;

            // Load the project file
            try
            {
                project = SynchronizerProject.Load(projectFilePath, args);
            }

            // Catch ArgumentException, which indicates that a command line parameter was missing
            // or invalid
            catch (ArgumentException exception)
            {
                Console.WriteLine(exception.Message + Environment.NewLine + usageString);
                return;
            }

            // Enable tracing for this application
            BasicConsoleTraceListener consoleTraceListener = new BasicConsoleTraceListener();
            consoleTraceListener.Filter = new EventTypeFilter(consoleOutputLevel);

            if (logTraceListener != null)
            {
                logTraceListener.Filter = new EventTypeFilter(logOutputLevel);
                project.TraceListeners.Add(logTraceListener);
            }

            project.TraceListeners.Add(consoleTraceListener);

            // Execute the project
            try
            {
                project.Execute();
            }

            // Write any uncaught exceptions to the console and log, if necessary
            catch (Exception exception)
            {
                consoleTraceListener.TraceEvent(null, "", TraceEventType.Critical, 0, exception.Message + Environment.NewLine + exception.StackTrace);

                if (logTraceListener != null)
                    logTraceListener.TraceEvent(null, "", TraceEventType.Critical, 0, exception.Message + Environment.NewLine + exception.StackTrace);
            }

            finally
            {
                // Close out the log
                if (logTraceListener != null)
                {
                    logTraceListener.Flush();

                    // Write the closing tag to the log file if necessary
                    if (logTraceListener is XmlWriterTraceListener)
                        logWriter.Write("</E2ETraceEvents>");

                    logWriter.Flush();
                }

                project.Dispose();
            }
        }
    }
}
